/**
 ******************************************************************************
 * @file    USB_Device/CDC_Standalone/Src/usbd_desc.c
 * @author  MCD Application Team
 * @brief   This file provides the USBD descriptors and string formating method.
 ******************************************************************************
 * @attention
 *
 * <h2><center>&copy; Copyright (c) 2017 STMicroelectronics.
 * All rights reserved.</center></h2>
 *
 * This software component is licensed by ST under Ultimate Liberty license
 * SLA0044, the "License"; You may not use this file except in compliance with
 * the License. You may obtain a copy of the License at:
 *                             www.st.com/SLA0044
 *
 ******************************************************************************
 */
/* Includes ------------------------------------------------------------------*/

#include "usbd_core.h"
#include "usbd_desc.h"
#include "usbd_conf.h"

/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
#define USBD_VID 0x9527
#define USBD_PID 0x959A
#define USBD_LANGID_STRING 1033
#define USBD_MANUFACTURER_STRING "zhouyu@4mychip.com"
#define USBD_PRODUCT_STRING_FS "WINUSB ZY"
#define USBD_CONFIGURATION_STRING_FS "WINUSB Config"
#define USBD_INTERFACE_STRING_FS "WINUSB Interface"

// USBD_LANGID_STRING 0x1404-ZH //0x0409-EN
#if (USBD_SUPPORT_WINUSB == 1) // class
uint8_t *USBD_WinUSBOSFeatureDescriptor(uint16_t *length);
uint8_t *USBD_WinUSBOSPropertyDescriptor(uint16_t *length);
#endif
/* USER CODE END PRIVATE_DEFINES */

/**
 * @}
 */

/* USER CODE BEGIN 0 */

#if defined(__ICCARM__) /* IAR Compiler */
#pragma data_alignment = 4
#endif /* defined ( __ICCARM__ ) */
/* Internal string descriptor. */
__ALIGN_BEGIN uint8_t USBD_StrDesc[USBD_MAX_STR_DESC_SIZ] __ALIGN_END;

#if (USBD_SUPPORT_WINUSB == 1) // desc_define

#define USB_LEN_OS_FEATURE_DESC 0x28
#if defined(__ICCARM__) /* IAR Compiler */
#pragma data_alignment = 4
#endif /* defined ( __ICCARM__ ) */
__ALIGN_BEGIN uint8_t USBD_WINUSB_OSFeatureDesc[USB_LEN_OS_FEATURE_DESC] __ALIGN_END =
    {
        USB_LEN_OS_FEATURE_DESC, 0, 0, 0,   // length
        0, 1,                               // bcd version 1.0
        4, 0,                               // windex: extended compat ID descritor
        1,                                  // no of function
        0, 0, 0, 0, 0, 0, 0,                // reserve 7 bytes
                                            // function
        0,                                  // interface no
        0,                                  // reserved
        'W', 'I', 'N', 'U', 'S', 'B', 0, 0, //  first ID
        0, 0, 0, 0, 0, 0, 0, 0,             // second ID
        0, 0, 0, 0, 0, 0                    // reserved 6 bytes
};

const uint8_t USBD_OS_STRING[8] = {'M', 'S', 'F', 'T', '1', '0', '0', USB_REQ_MS_VENDOR_CODE};

#define USB_LEN_OS_PROPERTY_DESC 0x8E
#if defined(__ICCARM__) /* IAR Compiler */
#pragma data_alignment = 4
#endif /* defined ( __ICCARM__ ) */
__ALIGN_BEGIN uint8_t USBD_WINUSB_OSPropertyDesc[USB_LEN_OS_PROPERTY_DESC] __ALIGN_END =
    {
        USB_LEN_OS_PROPERTY_DESC,
        0,
        0,
        0, // length 246 byte
        0x00,
        0x01, // BCD version 1.0
        0x05,
        0x00, // Extended Property Descriptor Index(5)
        0x01,
        0x00, // number of section (1)
              //; property section
        0x84,
        0x00,
        0x00,
        0x00, // size of property section
        0x1,
        0,
        0,
        0, //; property data type (1)
        0x28,
        0, //; property name length (42)
        'D',
        0,
        'e',
        0,
        'v',
        0,
        'i',
        0,
        'c',
        0,
        'e',
        0,
        'I',
        0,
        'n',
        0,
        't',
        0,
        'e',
        0,
        'r',
        0,
        'f',
        0,
        'a',
        0,
        'c',
        0,
        'e',
        0,
        'G',
        0,
        'U',
        0,
        'I',
        0,
        'D',
        0,
        0,
        0,
        // {13EB360B-BC1E-46CB-AC8B-EF3DA47B4062}
        0x4E,
        0,
        0,
        0, // ; property data length
        '{',
        0,
        '1',
        0,
        '3',
        0,
        'E',
        0,
        'B',
        0,
        '3',
        0,
        '6',
        0,
        '0',
        0,
        'B',
        0,
        '-',
        0,
        'B',
        0,
        'C',
        0,
        '1',
        0,
        'E',
        0,
        '-',
        0,
        '4',
        0,
        '6',
        0,
        'C',
        0,
        'B',
        0,
        '-',
        0,
        'A',
        0,
        'C',
        0,
        '8',
        0,
        'B',
        0,
        '-',
        0,
        'E',
        0,
        'F',
        0,
        '3',
        0,
        'D',
        0,
        'A',
        0,
        '4',
        0,
        '7',
        0,
        'B',
        0,
        '4',
        0,
        '0',
        0,
        '6',
        0,
        '2',
        0,
        '}',
        0,
        0,
        0,
};
#endif

/* Private macro -------------------------------------------------------------*/
/* Private function prototypes -----------------------------------------------*/

uint8_t *USBD_VCP_DeviceDescriptor(USBD_SpeedTypeDef speed, uint16_t *length);
uint8_t *USBD_VCP_LangIDStrDescriptor(USBD_SpeedTypeDef speed, uint16_t *length);
uint8_t *USBD_VCP_ManufacturerStrDescriptor(USBD_SpeedTypeDef speed, uint16_t *length);
uint8_t *USBD_VCP_ProductStrDescriptor(USBD_SpeedTypeDef speed, uint16_t *length);
uint8_t *USBD_VCP_SerialStrDescriptor(USBD_SpeedTypeDef speed, uint16_t *length);
uint8_t *USBD_VCP_ConfigStrDescriptor(USBD_SpeedTypeDef speed, uint16_t *length);
uint8_t *USBD_VCP_InterfaceStrDescriptor(USBD_SpeedTypeDef speed, uint16_t *length);

/* Private variables ---------------------------------------------------------*/
USBD_DescriptorsTypeDef FS_Desc = {
    USBD_VCP_DeviceDescriptor,
    USBD_VCP_LangIDStrDescriptor,
    USBD_VCP_ManufacturerStrDescriptor,
    USBD_VCP_ProductStrDescriptor,
    USBD_VCP_SerialStrDescriptor,
    USBD_VCP_ConfigStrDescriptor,
    USBD_VCP_InterfaceStrDescriptor,
#if (USBD_SUPPORT_WINUSB == 1) // class
    USBD_WinUSBOSFeatureDescriptor,
    USBD_WinUSBOSPropertyDescriptor
#endif
};

/**
 * Init USB device Library, add supported class and start the library
 * @retval None
 */
USBD_StatusTypeDef MX_USB_DEVICE_Init(USBD_HandleTypeDef *hUsbDeviceFS, USBD_CDC_ItfTypeDef *fops)
{
  /* USER CODE BEGIN USB_DEVICE_Init_PreTreatment */

  /* USER CODE END USB_DEVICE_Init_PreTreatment */

  /* Init Device Library, add supported class and start the library. */
  if (USBD_Init(hUsbDeviceFS, &FS_Desc, 0) != USBD_OK)
  {
    return USBD_FAIL;
  }
  if (USBD_RegisterClass(hUsbDeviceFS, &USBD_CDC) != USBD_OK)
  {
    return USBD_FAIL;
  }
  if (USBD_CDC_RegisterInterface(hUsbDeviceFS, fops) != USBD_OK)
  {
    return USBD_FAIL;
  }
  if (USBD_Start(hUsbDeviceFS) != USBD_OK)
  {
    return USBD_FAIL;
  }
  return USBD_OK;
}

/* USB Standard Device Descriptor */
#if defined(__ICCARM__) /*!< IAR Compiler */
#pragma data_alignment = 4
#endif
__ALIGN_BEGIN uint8_t USBD_DeviceDesc[USB_LEN_DEV_DESC] __ALIGN_END = {
    0x12,                 /* bLength */
    USB_DESC_TYPE_DEVICE, /* bDescriptorType */
    0x00,                 /* bcdUSB */
    0x02,
    0xFE, /*bDeviceClass*/ // 0x02 == CDC  0xFE == undefine未定义的杂项类别
    0x02,                  /* bDeviceSubClass */
    0x00,                  /* bDeviceProtocol */
    USB_MAX_EP0_SIZE,      /* bMaxPacketSize */
    LOBYTE(USBD_VID),      /* idVendor */
    HIBYTE(USBD_VID),      /* idVendor */
    LOBYTE(USBD_PID),      /* idVendor */
    HIBYTE(USBD_PID),      /* idVendor */
    0x00,                  /* bcdDevice rel. 2.00 */
    0x01,
    USBD_IDX_MFC_STR,          /* Index of manufacturer string */
    USBD_IDX_PRODUCT_STR,      /* Index of product string */
    USBD_IDX_SERIAL_STR,       /* Index of serial number string */
    USBD_MAX_NUM_CONFIGURATION /* bNumConfigurations */
}; /* USB_DeviceDescriptor */

/* USB Standard Device Descriptor */
#if defined(__ICCARM__) /*!< IAR Compiler */
#pragma data_alignment = 4
#endif
__ALIGN_BEGIN uint8_t USBD_LangIDDesc[USB_LEN_LANGID_STR_DESC] __ALIGN_END = {
    USB_LEN_LANGID_STR_DESC,
    USB_DESC_TYPE_STRING,
    LOBYTE(USBD_LANGID_STRING),
    HIBYTE(USBD_LANGID_STRING),
};

#if defined(__ICCARM__) /*!< IAR Compiler */
#pragma data_alignment = 4
#endif
__ALIGN_BEGIN uint8_t USBD_StringSerial[USB_SIZ_STRING_SERIAL] __ALIGN_END =
    {
        USB_SIZ_STRING_SERIAL,
        USB_DESC_TYPE_STRING,
};

#if defined(__ICCARM__) /*!< IAR Compiler */
#pragma data_alignment = 4
#endif
__ALIGN_BEGIN uint8_t USBD_StrDesc[USBD_MAX_STR_DESC_SIZ] __ALIGN_END;

/* Private functions ---------------------------------------------------------*/
static void IntToUnicode(uint32_t value, uint8_t *pbuf, uint8_t len);
static void Get_SerialNum(void);

#if (USBD_SUPPORT_WINUSB == 1) // function
uint8_t *USBD_WinUSBOSStrDescriptor(uint16_t *length)
{
  USBD_GetString((uint8_t *)USBD_OS_STRING, USBD_StrDesc, length);
  return USBD_StrDesc;
}
uint8_t *USBD_WinUSBOSFeatureDescriptor(uint16_t *length)
{
  *length = USB_LEN_OS_FEATURE_DESC;
  return USBD_WINUSB_OSFeatureDesc;
}
uint8_t *USBD_WinUSBOSPropertyDescriptor(uint16_t *length)
{
  *length = USB_LEN_OS_PROPERTY_DESC;
  return USBD_WINUSB_OSPropertyDesc;
}
#endif

/**
 * @brief  Returns the device descriptor.
 * @param  speed: Current device speed
 * @param  length: Pointer to data length variable
 * @retval Pointer to descriptor buffer
 */
uint8_t *USBD_VCP_DeviceDescriptor(USBD_SpeedTypeDef speed, uint16_t *length)
{
  /* Prevent unused argument(s) compilation warning */
  UNUSED(speed);

  *length = sizeof(USBD_DeviceDesc);
  return (uint8_t *)USBD_DeviceDesc;
}

/**
 * @brief  Returns the LangID string descriptor.
 * @param  speed: Current device speed
 * @param  length: Pointer to data length variable
 * @retval Pointer to descriptor buffer
 */
uint8_t *USBD_VCP_LangIDStrDescriptor(USBD_SpeedTypeDef speed, uint16_t *length)
{
  /* Prevent unused argument(s) compilation warning */
  UNUSED(speed);

  *length = sizeof(USBD_LangIDDesc);
  return (uint8_t *)USBD_LangIDDesc;
}

/**
 * @brief  Returns the product string descriptor.
 * @param  speed: Current device speed
 * @param  length: Pointer to data length variable
 * @retval Pointer to descriptor buffer
 */
uint8_t *USBD_VCP_ProductStrDescriptor(USBD_SpeedTypeDef speed, uint16_t *length)
{
  if (speed == 0)
  {
    USBD_GetString((uint8_t *)USBD_PRODUCT_STRING_FS, USBD_StrDesc, length);
  }
  else
  {
    USBD_GetString((uint8_t *)USBD_PRODUCT_STRING_FS, USBD_StrDesc, length);
  }
  return USBD_StrDesc;
}

/**
 * @brief  Returns the manufacturer string descriptor.
 * @param  speed: Current device speed
 * @param  length: Pointer to data length variable
 * @retval Pointer to descriptor buffer
 */
uint8_t *USBD_VCP_ManufacturerStrDescriptor(USBD_SpeedTypeDef speed, uint16_t *length)
{
  /* Prevent unused argument(s) compilation warning */
  UNUSED(speed);

  USBD_GetString((uint8_t *)USBD_MANUFACTURER_STRING, USBD_StrDesc, length);
  return USBD_StrDesc;
}

/**
 * @brief  Returns the serial number string descriptor.
 * @param  speed: Current device speed
 * @param  length: Pointer to data length variable
 * @retval Pointer to descriptor buffer
 */
uint8_t *USBD_VCP_SerialStrDescriptor(USBD_SpeedTypeDef speed, uint16_t *length)
{
  /* Prevent unused argument(s) compilation warning */
  UNUSED(speed);

  *length = USB_SIZ_STRING_SERIAL;

  /* Update the serial number string descriptor with the data from the unique ID*/
  Get_SerialNum();

  return (uint8_t *)USBD_StringSerial;
}

/**
 * @brief  Returns the configuration string descriptor.
 * @param  speed: Current device speed
 * @param  length: Pointer to data length variable
 * @retval Pointer to descriptor buffer
 */
uint8_t *USBD_VCP_ConfigStrDescriptor(USBD_SpeedTypeDef speed, uint16_t *length)
{
  if (speed == USBD_SPEED_HIGH)
  {
    USBD_GetString((uint8_t *)USBD_CONFIGURATION_STRING_FS, USBD_StrDesc, length);
  }
  else
  {
    USBD_GetString((uint8_t *)USBD_CONFIGURATION_STRING_FS, USBD_StrDesc, length);
  }
  return USBD_StrDesc;
}

/**
 * @brief  Returns the interface string descriptor.
 * @param  speed: Current device speed
 * @param  length: Pointer to data length variable
 * @retval Pointer to descriptor buffer
 */
uint8_t *USBD_VCP_InterfaceStrDescriptor(USBD_SpeedTypeDef speed, uint16_t *length)
{
  if (speed == 0)
  {
    USBD_GetString((uint8_t *)USBD_INTERFACE_STRING_FS, USBD_StrDesc, length);
  }
  else
  {
    USBD_GetString((uint8_t *)USBD_INTERFACE_STRING_FS, USBD_StrDesc, length);
  }
  return USBD_StrDesc;
}

/**
 * @brief  Create the serial number string descriptor
 * @param  None
 * @retval None
 */
static void Get_SerialNum(void)
{
  uint32_t deviceserial0, deviceserial1, deviceserial2;

  deviceserial0 = *(uint32_t *)DEVICE_ID1;
  deviceserial1 = *(uint32_t *)DEVICE_ID2;
  deviceserial2 = *(uint32_t *)DEVICE_ID3;

  deviceserial0 += deviceserial2;

  if (deviceserial0 != 0)
  {
    IntToUnicode(deviceserial0, &USBD_StringSerial[2], 8);
    IntToUnicode(deviceserial1, &USBD_StringSerial[18], 4);
  }
}

/**
 * @brief  Convert Hex 32Bits value into char
 * @param  value: value to convert
 * @param  pbuf: pointer to the buffer
 * @param  len: buffer length
 * @retval None
 */
static void IntToUnicode(uint32_t value, uint8_t *pbuf, uint8_t len)
{
  uint8_t idx = 0;

  for (idx = 0; idx < len; idx++)
  {
    if (((value >> 28)) < 0xA)
    {
      pbuf[2 * idx] = (value >> 28) + '0';
    }
    else
    {
      pbuf[2 * idx] = (value >> 28) + 'A' - 10;
    }

    value = value << 4;

    pbuf[2 * idx + 1] = 0;
  }
}
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
